package ms.unicorn.dsl.impl;

import java.util.ArrayList;
import java.util.List;

import ms.unicorn.dsl.ILanguageDiagram;
import ms.unicorn.dsl.ILanguageElement;
import ms.unicorn.dsl.ILanguageObject;
import ms.unicorn.dsl.IModelLanguage;
import ms.unicorn.dsl.runtime.ExecutionContext;
import ms.unicorn.dsl.runtime.SphynxInterpreter;
import ms.unicorn.dsl.runtime.VariableStorage;
import ms.unicorn.dsl.runtime.impl.DefaultExecutionContext;
import ms.unicorn.dsl.unicornDSL.DslConvertionScheme;
import ms.unicorn.dsl.unicornDSL.IdentifiedElement;
import ms.unicorn.dsl.unicornDSL.NamedProperty;
import ms.unicorn.dsl.unicornDSL.XExpression;
import ms.unicorn.dsl.unicornDSL.impl.UnicornLanguageImpl;

public class ModelLanguage extends UnicornLanguageImpl implements
		IModelLanguage {

	
	private List<ILanguageObject> objects;
	private List<ILanguageElement> elements;
	private List<ILanguageDiagram> diagrams;
	private String defaultDiagramExtension;
	private String text;
	private String description;
	
	public static final ExecutionContext context = new DefaultExecutionContext(null, null);
	public static final SphynxInterpreter interpreter = new SphynxInterpreter();
	
	static {
		context.allocateVariable(VariableStorage.READONLY, true, "console", System.out);
	}
	
	@Override
	public String getText() {
		if (text == null)
			text = getElementProperty(PROP_TEXT, getName());
		return text;
		
	}

	@Override
	public ExecutionContext getContext() {
		return context;
	}
	
	@SuppressWarnings("unchecked")
	public <T> T getElementProperty(String name, T defvalue, ExecutionContext context) {
		for (IdentifiedElement prop: this.getProperties())
			if (prop.getName().equalsIgnoreCase(name)) {
				return (T)interpreter.resolveValue(((NamedProperty) prop).getValue(), context);
			}
		return defvalue;
	}
	
	public <T> T getElementProperty(String name, T defvalue) {
		return getElementProperty(name, defvalue, context);
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> T evaluate(XExpression value) {
		return (T)interpreter.resolveValue(value, context);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public <T> T evaluate(XExpression value, ExecutionContext context) {
		return (T)interpreter.resolveValue(value, context);
	}

	public List<ILanguageObject> getLanguageObjects() {
		if (objects == null) {
			objects = new ArrayList<ILanguageObject>();
			elements = new ArrayList<ILanguageElement>();
			diagrams = new ArrayList<ILanguageDiagram>();
			for (XExpression e: this.getElements())
				if (e instanceof ILanguageObject) 
					switch(((ILanguageObject) e).getObjectType()) {
					case LANGUAGE_DIAGRAM:
						diagrams.add((ILanguageDiagram) e);
						break;
					case LANGUAGE_ENTITY:
					case LANGUAGE_RELATION:
						elements.add((ILanguageElement) e);
						break;
					default:
						objects.add((ILanguageObject) e);
					}
				
		}
		return objects;
	}
	
	@Override
	public List<ILanguageElement> getLanguageElements() {
		if (elements == null) {
			getLanguageObjects();
		}
		return elements;
	}

	@Override
	public List<ILanguageDiagram> getDiagrams() {
		if (diagrams == null) {
			getLanguageObjects();
		}
		return diagrams;
	}

	@Override
	public String getDefaultDiagramExtension() {
		if (defaultDiagramExtension == null) {
			defaultDiagramExtension = getElementProperty(LANG_DIAGRAM_EXT, LANG_DIAGRAM_EXT_DEFAULT);
		}
		return defaultDiagramExtension;
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T extends ILanguageElement> T getLanguageElement(String typeId) {
		for(ILanguageElement e: getLanguageElements()) 
			if (e.getName().equals(typeId)) return (T)e;
		return null;
	}

	public String getDescription() {
		if (description == null)
			description = getElementProperty(PROP_DESCRIPTION, getName());
		return description;
	}

	@Override
	public String getId() {
		return getName();
	} 
	
	@Override
	public ILanguageDiagram getDiagram(String typeId) {
		for (ILanguageDiagram diagram: getDiagrams())
			if (diagram.getName().equals(typeId))
				return diagram;
		return null;
	}

	@Override
	public DslConvertionScheme getConvertionScheme(String convertionScheme) {
		for (XExpression child: getElements()) {
			if (child instanceof DslConvertionScheme)
				if (convertionScheme.equals(((DslConvertionScheme) child).getName())) 
					return (DslConvertionScheme) child;
		}
		return null;
	}

	@Override
	public int getObjectType() {
		return LANGUAGE;
	}

}
